using System;
using System.Configuration;
using System.Diagnostics;
using System.Reflection;
using MoneyManagerNET.DAL.Mock;
using MoneyManagerNET.Model;

namespace App
{
    public class EntityBusLoader
    {
        private readonly TraceSource _trace;
        private Func<IEntityBus> _fallback;

        internal EntityBusLoader()
        {
            this._trace = new TraceSource("EntityBusLoader");

            this.Regard<MoneyManagerNET.DAL.Repository.EF4.EntityBus>()
                .Regard<MoneyManagerNET.DAL.Repository.EF4.SqlCe.EntityBus>()
                .Regard<EntityBus>().Fallback(() => new EntityBus());
        }

        private EntityBusLoader Regard<T>()
        {
            return this;
        }

        private void Fallback(Func<IEntityBus> fallback)
        {
            this._fallback = fallback;
        }

        public IEntityBus LoadEntityBus()
        {
            var assemblyName = ConfigurationManager.AppSettings["MoneyManagerNET.App.Settings.AssemblyName"];
            var className = ConfigurationManager.AppSettings["MoneyManagerNET.App.Settings.ClassName"];

            IEntityBus instance;

            if (!string.IsNullOrEmpty(assemblyName) && !string.IsNullOrEmpty(className))
            {
                try
                {
                    // Load specified entity bus
                    this._trace.TraceInformation("loading     asm: {0}     type: {1}", assemblyName, className);

                    var asm = Assembly.Load(assemblyName);
                    this._trace.TraceInformation("loaded asm: {0}", asm.FullName);

                    var type = asm.GetType(className);
                    this._trace.TraceInformation("loaded type: {0}", type.FullName);

                    instance = Activator.CreateInstance(type) as IEntityBus;
                    this._trace.TraceInformation("created instance");
                }
                catch (Exception ex)
                {
                    this._trace.TraceEvent(TraceEventType.Error, 0, "could not set up storage implementation: {0}", ex);
                    // In case of error: Load fallback entity bus
                    instance = this._fallback.Invoke();
                }
            }
            else
            {
                this._trace.TraceEvent(TraceEventType.Error, 0,
                                       "could not set up storage implementation: no storage implementation specified");
                // Load fallback entity bus
                instance = this._fallback.Invoke();
            }

            return instance;
        }
    }
}